package com.fdb.smcts.core.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringEscapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fdb.smcts.core.bean.TxtFileLoadBean;

/**
 * 开发人员：【chenxuan】
 * 创建/修改日期：【2019/5/16】
 * 功能描述：从文本读取文件，绑定数据对指定对象，并返回对象List
 *
 * @Param
 * @return
 **/
public class TxtFileLoadPlugin {
    private static Logger logger = LoggerFactory.getLogger(TxtFileLoadPlugin.class);
    // 文件路径
    private String fileUrl;
    /* 文件流(按缓冲区大小读取) */
    private BufferedReader br;
    /* 缓冲区大小(K) */
    private int loadBufferSize = 1024;
    /* 文件字段数据分隔符(注意在Java中有许多字符需要转义) */
    private String dataSplit = "";
    /* 文件编码格式 */
    private String fileCharset = "UTF-8";
    /* 去除文档开头行数 */
    private int IsFirstline = 0;
    /* 文件总共记录数统计 */
    private int fileRecordCount;

	/* 每次结束条数 */
    private int endCount;
    /* 每次读取条数 */
    private int readCount;
    /* 每次0条 */
    int start = 0;
    /**
     * 继续读取，插件会在第二次读取时赋值为true，以获取上次打开的文件流
     */
    public boolean isFirstRead = true;

    public TxtFileLoadPlugin(String fileUrl, String dataSplit, String fileCharset, int isFirstline, int readCount) {
        this.fileUrl = fileUrl;
        this.dataSplit = dataSplit;
        this.fileCharset = fileCharset;
        IsFirstline = isFirstline;
        this.readCount = readCount;
        this.endCount = readCount;
    }

    /**
     * 开发人员：【chenxuan】
     * 创建/修改日期：【2019/5/17】
     * 修改描述：读取文件到指定文件中
     *
     * @return java.util.List<com.fdb.efp.batch.service.domain.TxtFileLoadBean>
     * @Param [obj]
     **/
    public List<TxtFileLoadBean> run(TxtFileLoadBean obj) throws Exception {
        // 加载文件

        // 取得连接
        double before = System.currentTimeMillis();
        List<TxtFileLoadBean> list = new ArrayList<>(endCount - start + 1);
        try {
            if (isFirstRead) {
                loadTxt();
                // 构建文件流
                br = new BufferedReader(new InputStreamReader(new FileInputStream(
                        fileUrl), fileCharset), loadBufferSize * 1024);
                // 初始化分隔符
                dataSplit = StringEscapeUtils.escapeJava(dataSplit);
            }
            if (dataSplit.trim().equals("|")) {
                dataSplit = "\\" + dataSplit;
            } else if (dataSplit.trim().equals("||")) {
                dataSplit = "\\|\\\\|";
            }
            list = fetchDataToObject(start, endCount, obj);
            start = endCount + 1;
            endCount = endCount + readCount;
            //显示总共多少条记录,以及成功导入了多少条
            logger.info("已读取文件总记录数:" + fileRecordCount);
            double after = System.currentTimeMillis();
            logger.info("本次读取耗时[" + (after - before) / 1000 + "]秒");
        } catch (Exception e) {
            obj.isReadedComplete = true;
            logger.error("导入数据出错!", e);
            throw new RuntimeException(e);
        } finally {
            //关掉对象,释放资料
            try {
                //判断是否读取多最后一行或异常  关闭文件流
                if (obj.isReadedComplete) {
                    if (br != null) {
                        br.close();
                        br = null;
                    }
                }
            } catch (Exception e) {
                logger.error("关闭文件流对象时异常!", e);
                throw new RuntimeException(e);
            }

        }
        return list;
    }


    /**
     * 开发人员：【chenxuan】
     * 创建/修改日期：【2019/5/17】
     * 修改描述：加载文件
     *
     * @return void
     * @Param []
     **/
    public void loadTxt() throws Exception {
        // 判断文件是否都存在,不存在则直接置为失败
        File file = new File(fileUrl);
        if (file == null || !file.exists() || !file.isFile()) {
            logger.info("找不到要导入的文件:" + fileUrl);
            throw new RuntimeException("找不到要导入的文件:" + fileUrl);
        }
        // 检查文件大小,如果文件为零,则判断是否允许为空文件
        BigDecimal fileSize = new BigDecimal(file.length())
                .divide(BigDecimal.valueOf(1024.00 * 1024.00));
        logger.info("[文件大小(M)]:" + fileSize.toString());
        if (file.length() < 1) {
            throw new RuntimeException("文件数据为空:" + fileUrl);
        }
        logger.info("开始装载...");
    }

    /**
     * 开发人员：【chenxuan】
     * 创建/修改日期：【2019/5/17】
     * 修改描述：读取指定行数的记录并绑定到执行对象
     *
     * @return java.util.List<com.fdb.efp.batch.service.domain.TxtFileLoadBean>
     * @Param [start, end, txtFileLoadBean]
     **/
    public List<TxtFileLoadBean> fetchDataToObject(int start, int end, TxtFileLoadBean txtFileLoadBean) throws Exception {
        // 无法获取文本行数 ，无法指定List容量
        //  List<TxtFileLoadBean> list = new ArrayList<>(end-start+1);
        List<TxtFileLoadBean> list = new ArrayList<>();
        String s = null;
        try {
            //判断是否需要去除首行
            if (start == 0) {
                //去除首行
                for (int i = 0; i < IsFirstline; i++) {
                    s = br.readLine();
                }
            }
            s = br.readLine();
            // 得到总条数和是否已读完的标志
            if (s == null) {
                txtFileLoadBean.setReadedComplete(true);
            }
            // 读取数据
            while (s != null) {
                // 有可能数据中有换行，所以读出来是空行，但接着又会有一行是数据
                if (!"".equals(s.trim())) {
                    fileRecordCount++;
                    if (fileRecordCount >= start) {
                        TxtFileLoadBean tempLoanBean = txtFileLoadBean.clone();
                        tempLoanBean = bindValueToObject(tempLoanBean, s);
                        if (tempLoanBean != null) {
                            list.add(tempLoanBean);
                        }
                        if (fileRecordCount == end)
                            break;
                    }
                }
                s = br.readLine();
            }
            return list;
        } catch (IOException e) {
            throw new Exception(e);
        }
    }


    /**
     * 开发人员：【chenxuan】
     * 创建/修改日期：【2019/5/17】
     * 描述：绑定对象
     *
     * @return com.fdb.efp.batch.service.domain.TxtFileLoadBean
     * @Param [txtFileLoadBean, values]
     **/
    private TxtFileLoadBean bindValueToObject(TxtFileLoadBean txtFileLoadBean, String values) throws IllegalAccessException, InstantiationException {
        // 分解后得到一行包含的字段
        String[] frecord = values.split(dataSplit, -1);
        // 验证字段个数与数据个数是否匹配(最后一位必须带分隔符,所以减一)
        int dataLength = frecord.length + 1;
        Field[] field = txtFileLoadBean.getClass().getDeclaredFields();
        if (dataLength < field.length) {
            logger.info("字段个数与数据个数不匹配");
            throw new RuntimeException("字段个数与数据个数不匹配");
        }
        boolean isSerial = false;
        if ("serialVersionUID".equals(field[0].getName())) {
            // 不处理 序列化字段
            isSerial = true;
        }
        for (int i = 0; i < field.length; i++) {
            Field f = field[i];
            f.setAccessible(true);
            String type = f.getType().toString();
            if (type.endsWith("String")) {
                if(isSerial){
                    f.set(txtFileLoadBean, frecord[i-1]);
                }else {
                    f.set(txtFileLoadBean, frecord[i]);
                }
            }
            if (type.endsWith("int") || type.endsWith("Integer")) {
                if (isSerial){
                    if (StringUtil.isEmpty(frecord[i-1])) {
                        f.set(txtFileLoadBean, 0);
                    } else {
                        f.set(txtFileLoadBean, Integer.parseInt(frecord[i-1]));
                    }
                }else{
                    if (StringUtil.isEmpty(frecord[i])) {
                        f.set(txtFileLoadBean, 0);
                    } else {
                        f.set(txtFileLoadBean, Integer.parseInt(frecord[i]));
                    }
                }

            }
            if (type.endsWith("BigDecimal")) {
                if (isSerial) {
                    if (StringUtil.isEmpty(frecord[i-1])) {
                        f.set(txtFileLoadBean, null);
                    } else {
                        f.set(txtFileLoadBean, new BigDecimal(frecord[i-1]));
                    }
                }else{
                    if (StringUtil.isEmpty(frecord[i])) {
                        f.set(txtFileLoadBean, null);
                    } else {
                        f.set(txtFileLoadBean, new BigDecimal(frecord[i]));
                    }
                }
            }
        }
        return txtFileLoadBean;
    }

    public void setFirstRead(boolean firstRead) {
        isFirstRead = firstRead;
    }
    public int getFileRecordCount() {
    	return fileRecordCount;
    }
    
    public void setFileRecordCount(int fileRecordCount) {
    	this.fileRecordCount = fileRecordCount;
    }

}
